/*
 * Copyright (c) JoinQuant Development Team
 * Author: Huayong Kuang <kuanghuayong@joinquant.com>
 */

#include <stdio.h>  // for sprintf
#include <sys/time.h>  // for gettimeofday
#include <math.h>  // for lrint
#include <iostream>
#include <string>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <chrono>

using namespace std;

struct tm getLocalTime() {
    time_t rawtime;
    struct tm tminfo;
    time(&rawtime);
    localtime_r(&rawtime, &tminfo);
    return tminfo;
}

string getCurrentTime() {
    char buf[20];
    struct timeval tv;
    time_t curtime;

    gettimeofday(&tv, NULL);
    curtime = tv.tv_sec;

    strftime(buf, 20, "%H:%M:%S", localtime(&curtime));
    return string(buf);
}

string getCurrentTime2() {
    char buf[20], buf2[30];
    struct timeval tv;
    int millisec;
    time_t curtime;

    gettimeofday(&tv, NULL);

    millisec = lrint(tv.tv_usec / 1000.0);  // Round to nearest millisec
    if (millisec >= 1000) {  // Allow for rounding up to nearest second
        millisec -= 1000;
        tv.tv_sec++;
    }

    curtime = tv.tv_sec;
    strftime(buf, 20, "%H:%M:%S", localtime(&curtime));
    sprintf(buf2, "%s.%03d", buf, millisec);
    return string(buf2);
}

string getCurrentTime3() {
    char buf[20], buf2[30];
    struct timeval tv;
    time_t curtime;

    gettimeofday(&tv, NULL);
    curtime = tv.tv_sec;

    strftime(buf, 20, "%H:%M:%S", localtime(&curtime));
    sprintf(buf2, "%s.%06ld", buf, tv.tv_usec);
    return string(buf2);
}

string getCurrentTime4() {
    using namespace std::chrono;

    // get current time
    auto now = system_clock::now();

    // get number of milliseconds for the current second
    // (remainder after division into seconds)
    auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;

    // convert to std::time_t in order to convert to std::tm (broken time)
    auto timer = system_clock::to_time_t(now);

    // convert to broken time
    std::tm bt = *localtime(&timer);

    std::ostringstream oss;

    oss << put_time(&bt, "%H:%M:%S");  // HH:MM:SS
    oss << '.' << std::setfill('0') << std::setw(3) << ms.count();

    return oss.str();
}

string getCurrentTime5() {
    using namespace std::chrono;

    // 获取当前时间
    auto now = system_clock::now();

    // 记录上一次获取的时间，当前时间比上一次时间小时则重新获取
    // 偶尔获取的时间会差 8 小时，这可能是内核的问题
    static auto last_clock = now;
    duration<double> diff = now - last_clock;
    while (diff.count() < 0) {
        now = system_clock::now();
        diff = now - last_clock;
    }
    last_clock = now;
    std::cout << "diff: " << diff.count() << endl;

    // 获取当前毫秒数
    auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;

    // 转换为 std::tm (broken time) 时间
    auto timer = system_clock::to_time_t(now);
    std::tm bt = *localtime(&timer);

    // 格式化为分钟
    std::ostringstream oss;
    oss << put_time(&bt, "%H:%M:%S");  // HH:MM:SS
    oss << '.' << std::setfill('0') << std::setw(3) << ms.count();

    return oss.str();
}


int main(void)
{
    struct tm lt = getLocalTime();
    printf("current: %04d-%02d-%02d %02d:%02d:%02d\n",
           lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
           lt.tm_hour, lt.tm_min,lt.tm_sec);

    cout << getCurrentTime() << endl;
    cout << getCurrentTime2() << endl;
    cout << getCurrentTime3() << endl;
    cout << getCurrentTime4() << endl;

    for (int i = 0; i < 100; i++) {
        cout << getCurrentTime5() << endl;
    }

    return 0;
}
